﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

#region

using Microsoft.Extensions.Caching.Distributed;

#endregion

namespace Neto.Caches;

/// <summary>
///     依赖缓存扩展
/// </summary>
public static class CacheX
{
    /// <summary>
    ///     缓存前缀
    /// </summary>
    public static string CachePrefix { get; } = "Neto_Cache_";

    /// <summary>
    ///     获取缓存key
    /// </summary>
    /// <typeparam name="TEntity">实体名称</typeparam>
    /// <returns></returns>
    public static string GetTableCacheKey<TEntity>()
    {
        var entityName = typeof(TEntity).Name;
        var cachekey = $"{CachePrefix}Table_{entityName}";
        return cachekey;
    }

    /// <summary>
    ///     获取缓存key
    /// </summary>
    /// <typeparam name="dbTableName">实体名称</typeparam>
    /// <returns></returns>
    public static string GetTableCacheKey(string dbTableName)
    {
        var cachekey = $"{CachePrefix}Table_{dbTableName}";
        return cachekey;
    }

    /// <summary>
    ///     获取缓存key
    /// </summary>
    /// <typeparam name="key">缓存的Key</typeparam>
    /// <returns></returns>
    public static string GetCacheKey(string key)
    {
        return $"{CachePrefix}{key}";
    }

    /// <summary>
    ///     获取默认内存缓存选项
    /// </summary>
    /// <returns></returns>
    private static DistributedCacheEntryOptions GetDefaultDistributedCacheEntryOptions()
    {
        var options = new DistributedCacheEntryOptions
        {
            //SlidingExpiration = new System.TimeSpan(1, 1, 1, 1),
            //AbsoluteExpirationRelativeToNow= new System.TimeSpan(1, 1, 1, 1),
            //AbsoluteExpiration=DateTimeOffset.Now.AddDays(10)
        };

        return options;
    }

    #region 缓存Key

    //主要用于特殊情况，比如sqlsugar的缓存继承封装需要用到所有的缓存keys，但是依赖缓存又没有提供此方法接口获取

    // /// <summary>
    // /// 设置缓存Key
    // /// </summary>
    // /// <param name="cacheKey"></param>
    // private static void SetCacheKey(string cacheKey)
    // {
    //     //var cacheKeys = GetAllDicCacheKeys();
    //     //if (cacheKeys.ContainsKey(cacheKey)) return;
    //     //cacheKeys.TryAdd(cacheKey, cacheKey);
    //     //Set("System_Default_CacheKeys", cacheKeys.Keys.ToList());
    // }

    ///// <summary>
    ///// 移除缓存Key
    ///// </summary>
    ///// <param name="cacheKey"></param>
    //private static void RemoveCacheKey(string cacheKey)
    //{
    //    var cacheKeys = GetAllDicCacheKeys();
    //    if (cacheKeys.ContainsKey(cacheKey)) cacheKeys.TryRemove(cacheKey, out _);
//
    //    Set("System_Default_CacheKeys", cacheKeys.Keys.ToList());
    //}
//
    // /// <summary>
    // /// 移除缓存Key
    // /// </summary>
    // /// <param name="removeCacheKeys">缓存Key集合</param>
    // private static void RemoveCacheKey(List<string> removeCacheKeys)
    // {
    //     var cacheKeys = GetAllDicCacheKeys();
    //     foreach (var cacheKey in removeCacheKeys.Where(cacheKey => cacheKeys.ContainsKey(cacheKey)))
    //         cacheKeys.TryRemove(cacheKey, out _);
    //     Set("System_Default_CacheKeys", cacheKeys.Keys.ToList());
    // }

    ///// <summary>
    ///// 获取线程安全的缓存Key集合
    ///// </summary>
    ///// <returns></returns>
    //private static ConcurrentDictionary<string, string> GetAllDicCacheKeys()
    //{
    //    var cacheKeys = GetAllCacheKeys() ?? new List<string>();
    //    var dic = new ConcurrentDictionary<string, string>();
    //    foreach (var key in cacheKeys) dic.TryAdd(key, key);
    //    return dic;
    //}//
    ///// <summary>
    ///// ///
    ///// <summary>
    /////     获取所有的缓存Key【实际上可能并不是全部，因为部分地方可能并没有使用Caches类来操作缓存，这部分没有获取到】
    ///// </summary>
    ///// </summary>
    ///// <param name="prefix">前缀，默认为空，返回全部</param>
    ///// <returns></returns>
    //public static List<string> GetAllCacheKeys(string prefix = "")
    //{
    //    var cacheKeys = Get<List<string>>("System_Default_CacheKeys");
    //    if (cacheKeys == null) return [];
    //    if (string.IsNullOrWhiteSpace(prefix))
    //        return cacheKeys;
    //    return cacheKeys.Where(o => o.StartsWith(prefix)).ToList();
    //}

    /// <summary>
    ///     获取内存缓存Key集合
    /// </summary>
    /// <returns></returns>
    public static IEnumerable<string> GetMemoryCacheKeys()
    {
        return MemoryCacheHelper.GetAllKeys();
    }

    #endregion 缓存Key

    #region 同步设置缓存

    /// <summary>
    ///     设置缓存
    /// </summary>
    /// <param name="cachekey">缓存Key</param>
    /// <param name="obj">对象</param>
    /// <param name="slidingExpirationSeconds">缓存秒数，滑动过期秒数</param>
    /// <param name="absoluteExpirationSeconds">缓存秒数，绝对过期秒数</param>
    public static void Set(string cachekey, object obj, int slidingExpirationSeconds, int absoluteExpirationSeconds = 0)
    {
        var options = GetDefaultDistributedCacheEntryOptions();
        //设置滑动过期时间
        if (slidingExpirationSeconds > 0) options.SlidingExpiration = new TimeSpan(0, 0, slidingExpirationSeconds);
        //设置绝对过期时间
        if (absoluteExpirationSeconds > 0)
            options.AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(absoluteExpirationSeconds);

        //如果都没有设置，表示长期缓存，不过期。。。除非手动操作

        Set(cachekey, obj, options);
    }

    /// <summary>
    ///     设置缓存
    /// </summary>
    /// <param name="cachekey">缓存Key</param>
    /// <param name="obj">对象</param>
    /// <param name="slidingExpiration">设置缓存项在被删除之前可以处于非活动状态的时间，生存期不会延长到绝对过期</param>
    /// <param name="absoluteExpirationRelativeToNow">获取或设置相对于现在的绝对过期时间</param>
    /// <param name="absoluteExpiration">获取或设置缓存项的绝对过期日期</param>
    public static void Set(string cachekey, object obj, TimeSpan? slidingExpiration = null,
        TimeSpan? absoluteExpirationRelativeToNow = null, DateTimeOffset? absoluteExpiration = null)
    {
        var options = GetDefaultDistributedCacheEntryOptions();
        //设置滑动过期时间
        if (slidingExpiration != null) options.SlidingExpiration = slidingExpiration;
        //设置绝对过期时间
        if (absoluteExpirationRelativeToNow != null)
            options.AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow;
        //设置绝对过期时间
        if (absoluteExpiration != null) options.AbsoluteExpiration = absoluteExpiration;

        //如果都没有设置，表示长期缓存，不过期。。。除非手动操作

        Set(cachekey, obj, options);
    }

    /// <summary>
    ///     设置缓存
    /// </summary>
    /// <param name="cachekey">缓存Key</param>
    /// <param name="obj">对象</param>
    /// <param name="options">缓存选项</param>
    public static void Set(string cachekey, object obj, DistributedCacheEntryOptions? options)
    {
        var cache = App.GetService<IDistributedCache>();
        options ??= GetDefaultDistributedCacheEntryOptions();
        var jsonstring = obj.ToJson();
        cache.SetString(cachekey, jsonstring, options);
    }

    #endregion 同步设置缓存

    #region 异步设置缓存

    /// <summary>
    ///     设置缓存
    /// </summary>
    /// <param name="cachekey">缓存Key</param>
    /// <param name="obj">对象</param>
    /// <param name="slidingExpirationSeconds">缓存秒数，滑动过期秒数</param>
    /// <param name="absoluteExpirationSeconds">缓存秒数，绝对过期秒数</param>
    /// <param name="token">取消令牌</param>
    public static async Task SetAsync(string cachekey, object obj, int slidingExpirationSeconds,
        int absoluteExpirationSeconds = 0, CancellationToken token = default)
    {
        var options = GetDefaultDistributedCacheEntryOptions();
        //设置滑动过期时间
        if (slidingExpirationSeconds > 0) options.SlidingExpiration = new TimeSpan(0, 0, slidingExpirationSeconds);
        //设置绝对过期时间
        if (absoluteExpirationSeconds > 0)
            options.AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(absoluteExpirationSeconds);

        //如果都没有设置，表示长期缓存，不过期。。。除非手动操作
        await SetAsync(cachekey, obj, options, token);
    }

    /// <summary>
    ///     设置缓存
    /// </summary>
    /// <param name="cachekey">缓存Key</param>
    /// <param name="obj">对象</param>
    /// <param name="slidingExpiration">设置缓存项在被删除之前可以处于非活动状态的时间，生存期不会延长到绝对过期</param>
    /// <param name="absoluteExpirationRelativeToNow">获取或设置相对于现在的绝对过期时间</param>
    /// <param name="absoluteExpiration">获取或设置缓存项的绝对过期日期</param>
    /// <param name="token">取消令牌</param>
    public static async Task SetAsync(string cachekey, object obj, TimeSpan? slidingExpiration = null,
        TimeSpan? absoluteExpirationRelativeToNow = null, DateTimeOffset? absoluteExpiration = null,
        CancellationToken token = default)
    {
        var options = GetDefaultDistributedCacheEntryOptions();
        //设置滑动过期时间
        if (slidingExpiration != null) options.SlidingExpiration = slidingExpiration;
        //设置绝对过期时间
        if (absoluteExpirationRelativeToNow != null)
            options.AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow;
        //设置绝对过期时间
        if (absoluteExpiration != null) options.AbsoluteExpiration = absoluteExpiration;

        //如果都没有设置，表示长期缓存，不过期。。。除非手动操作
        await SetAsync(cachekey, obj, options, token);
    }

    /// <summary>
    ///     设置缓存
    /// </summary>
    /// <param name="cachekey">缓存Key</param>
    /// <param name="obj">对象</param>
    /// <param name="options">缓存选项</param>
    /// <param name="token">取消令牌</param>
    public static async Task SetAsync(string cachekey, object obj, DistributedCacheEntryOptions? options,
        CancellationToken token = default)
    {
        var cache = App.GetService<IDistributedCache>();
        options ??= GetDefaultDistributedCacheEntryOptions();
        var jsonstring = obj.ToJson();
        await cache.SetStringAsync(cachekey, jsonstring, options, token);
    }

    #endregion 异步设置缓存

    #region 获取缓存

    //public static TEntity Get<TEntity>(string cacheKey,Func<TEntity> func)
    //{
    //    var _cache = App.GetService<IDistributedCache>();
    //    var databytes = _cache.GetString(cacheKey);
    //    if (string.IsNullOrWhiteSpace(databytes))
    //    {
    //       // return default;
    //       if(func!=null)
    //        {
    //            var a=func.Invoke();
    //        }

    //    }
    //    var entities = databytes.ToObj<TEntity>();
    //    return entities;
    //}

    /// <summary>
    ///     获取缓存数据
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <param name="cacheKey">缓存Key</param>
    /// <param name="func">方法</param>
    /// <param name="options">缓存配置选项</param>
    /// <returns>返回的实体对象</returns>
    public static TEntity? Get<TEntity>(string cacheKey, Func<TEntity>? func = null,
        DistributedCacheEntryOptions? options = null)
    {
        var cache = App.GetService<IDistributedCache>();
        var databytes = cache.GetString(cacheKey);
        if (string.IsNullOrWhiteSpace(databytes))
        {
            if (func == null) return default;
            var entities2 = func.Invoke();

            if (entities2 == null) return entities2;
            if (options == null)
            {
                options = GetDefaultDistributedCacheEntryOptions();
                options.SlidingExpiration = new TimeSpan(0, 10, 0);
            }

            Set(cacheKey, entities2, options);

            return entities2;
        }

        var entities = databytes.ToObj<TEntity>();
        return entities;
    }

    /// <summary>
    ///     获取缓存数据
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <param name="cacheKey">缓存Key</param>
    /// <param name="func">方法</param>
    /// <param name="options">缓存配置选项</param>
    /// <param name="token">取消令牌</param>
    /// <returns>返回的实体对象</returns>
    public static async Task<TEntity?> GetAsync<TEntity>(string cacheKey, Func<Task<TEntity>>? func = null,
        DistributedCacheEntryOptions? options = null, CancellationToken token = default)
    {
        var cache = App.GetService<IDistributedCache>();
        var databytes = await cache.GetStringAsync(cacheKey, token);
        if (string.IsNullOrWhiteSpace(databytes))
        {
            if (func == null) return default;
            var entities2 = await func.Invoke();

            if (entities2 == null) return entities2;
            if (options == null)
            {
                options = GetDefaultDistributedCacheEntryOptions();
                options.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(10);
            }

            await SetAsync(cacheKey, entities2, options, token);

            return entities2;
        }

        var entities = databytes.ToObj<TEntity>();
        return entities;
    }

    #endregion 获取缓存

    #region 移除缓存

    /// <summary>
    ///     移除缓存
    /// </summary>
    /// <param name="cacheKey">缓存Key</param>
    public static void Remove(string cacheKey)
    {
        var cache = App.GetService<IDistributedCache>();
        cache.Remove(cacheKey);
        //RemoveCacheKey(cacheKey);
    }

    /// <summary>
    ///     移除缓存
    /// </summary>
    /// <param name="cacheKey">缓存Key</param>
    /// <param name="token"></param>
    public static async Task RemoveAsync(string cacheKey, CancellationToken token = default)
    {
        var cache = App.GetService<IDistributedCache>();
        await cache.RemoveAsync(cacheKey, token);
        //RemoveCacheKey(cacheKey);
    }

    // /// <summary>
    // /// 移除缓存Key,根据缓存前缀
    // /// </summary>
    // /// <param name="prefix">缓存前缀</param>
    // public static void RemoveByPrefix(string prefix)
    // {
    //     var cacheKeys = GetAllCacheKeys(prefix);
    //     var _cache = App.GetService<IDistributedCache>();
    //     foreach (var cacheKey in cacheKeys) _cache.Remove(cacheKey);
    //     RemoveCacheKey(cacheKeys);
    // }
    //
    // /// <summary>
    // /// 移除缓存Key,根据缓存前缀
    // /// </summary>
    // /// <param name="prefix">缓存前缀</param>
    // /// <param name="token"></param>
    // public static async Task RemoveByPrefixAsync(string prefix, CancellationToken token = default)
    // {
    //     var cacheKeys = GetAllCacheKeys(prefix);
    //     var _cache = App.GetService<IDistributedCache>();
    //     foreach (var cacheKey in cacheKeys) await _cache.RemoveAsync(cacheKey, token);
    //     RemoveCacheKey(cacheKeys);
    // }

    #endregion 移除缓存

    #region 刷新缓存 滑动过期时间

    /// <summary>
    ///     刷新缓存(重置滑动过期时间)
    /// </summary>
    /// <param name="cacheKey">缓存Key</param>
    public static void Refresh(string cacheKey)
    {
        var cache = App.GetService<IDistributedCache>();
        cache.Refresh(cacheKey);
    }

    /// <summary>
    ///     刷新缓存(重置滑动过期时间)
    /// </summary>
    /// <param name="cacheKey">缓存Key</param>
    /// <param name="token">取消令牌</param>
    public static async Task RefreshAsync(string cacheKey, CancellationToken token = default)
    {
        var cache = App.GetService<IDistributedCache>();
        await cache.RefreshAsync(cacheKey, token);
    }

    #endregion 刷新缓存 滑动过期时间

    #region 判断缓存是否存在

    /// <summary>
    ///     判断缓存是否存在
    /// </summary>
    /// <param name="cacheKey">缓存Key</param>
    /// <returns>返回的bool</returns>
    public static bool Exist(string cacheKey)
    {
        var cache = App.GetService<IDistributedCache>();
        var data = cache.GetString(cacheKey);
        return data != null;
    }

    /// <summary>
    ///     判断缓存是否存在
    /// </summary>
    /// <param name="cacheKey">缓存Key</param>
    /// <param name="token">取消令牌</param>
    /// <returns>返回的bool</returns>
    public static async Task<bool> ExistAsync(string cacheKey, CancellationToken token = default)
    {
        var cache = App.GetService<IDistributedCache>();

        var data = await cache.GetStringAsync(cacheKey, token);
        return data != null;
    }

    #endregion 判断缓存是否存在
}